import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

class ForContext
{
    ForContext out;
    BlockIr postBlock;
    BlockIr finalBlock;
}

class ReturnContext
{
    List<Variable> values;
}

class Context
{
    OutputStreamWriter writer;
    IrGenerator generator;
}

class StatementContext extends Context
{
    ForContext forContext;
    ReturnContext returnContext;
}

class AssignContext
{
    AssignContext out;
    List<Variable> assignValues = new ArrayList<>();
}

class ExprContext extends Context
{
    BlockIr breakBlock;
    BlockIr orBlock;
    AssignContext assignContext;
    boolean ok;

    ExprContext(){}

    ExprContext(StatementContext context)
    {
        writer = context.writer;
        generator = context.generator;
    }

    ExprContext(Context context)
    {
        writer = context.writer;
        generator = context.generator;
    }
}

public class printIr
{

    public static void printFunc(IrGenerator generator, FuncIr funcIr, OutputStreamWriter writer) throws IOException
    {
        printSymbolTable(generator, "func: " + funcIr.func.name, funcIr.func.symbolTable,writer);
        println(writer, 0, "funcIr: " + funcIr.func.name);
        printBlock(generator, funcIr.block, writer);
    }

    public static void printBlock(IrGenerator generator, BlockIr blockIr, OutputStreamWriter writer) throws IOException
    {
        while(blockIr != null)
        {
            println(writer, 1, "blockId: " + blockIr.blockId);
            QuadIr quadIr = blockIr.first;
            while(quadIr != null)
            {
                printQuad(generator, quadIr, writer);
                quadIr = quadIr.next;
            }
            blockIr = blockIr.next;
        }
    }

    public static void printQuad(IrGenerator generator, QuadIr quadIr, OutputStreamWriter writer) throws IOException
    {
        println(writer, 2, "quadId: " + quadIr.id + "; " + printValueIr(generator, quadIr.res) + "= " + printValueIr(generator, quadIr.arg1) + " "
        + quadIr.op.toString() + " " + printValueIr(generator, quadIr.arg2));
    }

    public static String printValueIr(IrGenerator generator, ValueIr valueIr)
    {
        if(valueIr == null)
            return "";
        switch (valueIr.type)
        {
            case Var:
                if(valueIr.value instanceof Variable)
                {
                    Variable variable = (Variable) valueIr.value;
                    return variable.name + ": " + generator.getKind((Expr) variable.type);
                }
                else if(valueIr.value instanceof ScopeObject)
                {
                    ScopeObject scopeObject = (ScopeObject) valueIr.value;
                    return scopeObject.name;
                }
            case Block:
                BlockIr blockIr = (BlockIr) valueIr.value;
                return "block: " + blockIr.blockId ;
            case Func:
                FuncIr funcIr = (FuncIr) valueIr.value;
                return "func: " + funcIr.func.name ;
            case Package:
                PackageIr packageIr = (PackageIr) valueIr.value;
                return "package: " + packageIr.name;
            case Int:
                int value = (int) valueIr.value;
                return String.valueOf(value);
        }
        return "";
    }

    public static void println(OutputStreamWriter writer, int layer, String src) throws IOException
    {
        for(int i=0; i<layer; i++)
            writer.write("  ");
        writer.write(src + "\n");
    }

    public static void printSymbolTable(IrGenerator generator, String name, SymbolTable table, OutputStreamWriter writer) throws IOException
    {
        if(table == null)
            return;
        println(writer, 0, "symbol table: " + name);
        println(writer, 0, "types");
        for(Object obj : table.types.values())
        {
            ScopeObject type = (ScopeObject) obj;
            StringBuilder str = new StringBuilder("name:" + type.name);
            String typeString = generator.getKind(((TypeDecl) type.field).type);
            switch(typeString)
            {
                case "Struct":
                    Object structType = generator.getRealType(((TypeDecl) type.field).type);
                    StructExpr structExpr = (StructExpr) structType;
                    str.append("{");
                    for(Field field : structExpr.fields)
                    {
                        str.append(" field:").append(field.id.name).append(",type:").append(generator.getKind(field.type));
                    }
                    str.append("}");
                    println(writer, 1, str.toString());
                default:println(writer, 1, "name:" + type.name + "[" + typeString + "]");
            }
        }

        println(writer, 0, "values");
        for(Object obj : table.vars.values())
        {
            ScopeObject scopeObject = (ScopeObject) obj;
            StringBuilder str = new StringBuilder("name:" + scopeObject.name);
            String typeString = generator.getKind((Expr) ((Variable)scopeObject.value).type);
            switch(typeString)
            {
                case "Struct":
                    Object structType = generator.getRealType((Expr)((Variable)scopeObject.value).type);
                    StructExpr structExpr = (StructExpr) structType;
                    str.append("{");
                    for(Field field : structExpr.fields)
                    {
                        str.append(" field:").append(field.id.name).append(",type:").append(generator.getKind(field.type));
                    }
                    str.append("}");
                    println(writer, 1, str.toString());
                default:println(writer, 1, "name:" + scopeObject.name + "[" + typeString + "]");
            }
        }
    }

    public static void printFunction(IrGenerator generator, FuncIr funcIr, OutputStreamWriter writer) throws IllegalAccessException, IOException
    {
        generator.currentFunctionIr = funcIr;
        generator.currentBlock = null;
        generator.currentQuad = null;
        funcIr.block = generator.newBlock();
        for(Statement statement : funcIr.func.statements)
        {
            StatementContext statementContext = new StatementContext();
            statementContext.writer = writer;
            statementContext.generator = generator;
            statementContext.returnContext = new ReturnContext();
            statementContext.returnContext.values = funcIr.func.results;
            printStatement(statement, statementContext);
        }
        generator.newQuad(null, null, null, OPIr.LastRetIr);
        printFunc(generator, funcIr, writer);
    }

    public static void printStatement(Statement statement, StatementContext ctx) throws IllegalAccessException
    {
        if(statement == null)
            return;
        switch(statement.getClass().getName())
        {
            case "EmptyStatement": break;
            case "ExpressionStatement":
                ExpressionStatement expressionStatement = (ExpressionStatement) statement;
                printExpr(expressionStatement.expr, new ExprContext(ctx));
                break;
            case "DeclStatement":
                DeclStatement declStatement = (DeclStatement) statement;
                printDecl(declStatement, ctx);
                break;
            case "AssignStatement":
                AssignStatement assignStatement = (AssignStatement) statement;
                boolean isMulti = false;
                if(assignStatement.left.size() > 1 && assignStatement.right.size() == 1)
                    isMulti = true;
                if(isMulti)
                {
                    switch(assignStatement.right.get(0).getClass().getName())
                    {
                        case "CallExpr":
                             CallExpr callExpr = (CallExpr)assignStatement.right.get(0);
                             AssignContext assignContext = new AssignContext();
                             assignContext.assignValues = new ArrayList<>();
                             for(Expr expr : assignStatement.left)
                             {
                                 Id id = null;
                                 if(expr.getClass().getName().equals("Id"))
                                    id = (Id)expr;
                                 else if(expr.getClass().getName().equals("SelectorExpr"))
                                 {
                                    SelectorExpr selectorExpr = (SelectorExpr) expr;
                                    id = (Id) selectorExpr.expr;
                                 }
                                 else
                                    System.out.println("Call expr id error");
                                 assert id != null;
                                 ScopeObject scopeObject = (ScopeObject) id.object;
                                 assignContext.assignValues.add((Variable) scopeObject.value);
                             }
                             ExprContext exprContext = new ExprContext(ctx);
                             exprContext.assignContext = assignContext;
                             printExpr(assignStatement.right.get(0), exprContext);
                             break;
                    }
                }
                else
                {
                    for(int i=0; i<assignStatement.left.size(); i++)
                    {
                        QuadIr quadIr = printExpr(assignStatement.left.get(i), new ExprContext(ctx));
                        AssignContext assignContext = new AssignContext();
                        assignContext.assignValues = new ArrayList<>();
                        assignContext.assignValues.add((Variable) quadIr.res.value);
                        ExprContext exprContext = new ExprContext(ctx);
                        exprContext.assignContext = assignContext;
                        QuadIr right = printExpr(assignStatement.right.get(i), exprContext);
                        if(exprContext.ok)
                            continue;
                        ctx.generator.newQuad(quadIr.res, right.res, null, OPIr.AssignIr);
                    }
                }
                break;
            case "ReturnStatement":
                ReturnStatement returnStatement = (ReturnStatement) statement;
                int index = 0;
                for(Expr result : returnStatement.results)
                {
                    QuadIr quadIr = printExpr(result, new ExprContext(ctx));
                    ValueIr valueIr = new ValueIr();
                    valueIr.value = ctx.returnContext.values.get(index);
                    valueIr.type = ValueIr.ValueType.Var;
                    ctx.generator.newQuad(valueIr, quadIr.res, null, OPIr.RetIr);
                    index++;
                }
                break;
            case "IFStatement":
                IFStatement ifStatement = (IFStatement) statement;
                printStatement(ifStatement.init, ctx);
                BlockIr currentBlock = ctx.generator.currentBlock;
                BlockIr ifBlock = ctx.generator.newBlock();
                BlockIr elseBlock = ctx.generator.newBlock();
                BlockIr finalBlock = ctx.generator.newBlock();
                ctx.generator.changeBlock(currentBlock);
                ExprContext exprContext = new ExprContext(ctx);
                exprContext.orBlock = ifBlock;
                exprContext.breakBlock = elseBlock;
                QuadIr quadIr = printExpr(ifStatement.condition, exprContext);
                ctx.generator.newQuad(null, quadIr.res, ctx.generator.newBlockValue(elseBlock), OPIr.IfFalseIr);
                ctx.generator.newQuad(null, ctx.generator.newBlockValue(ifBlock), null, OPIr.GoToIr);
                ctx.generator.changeBlock(ifBlock);
                printStatement(ifStatement.block, ctx);
                ctx.generator.newQuad(null, ctx.generator.newBlockValue(finalBlock), null, OPIr.GoToIr);
                ctx.generator.changeBlock(elseBlock);
                printStatement(ifStatement.elsePart, ctx);
                ctx.generator.newQuad(null, ctx.generator.newBlockValue(finalBlock), null, OPIr.GoToIr);
                ctx.generator.changeBlock(finalBlock);
                break;
            case "BlockStatement":
                BlockStatement blockStatement = (BlockStatement) statement;
                for(Statement statement1 : blockStatement.list)
                    printStatement(statement1, ctx);
                break;
            case "ForStatement":
                ForStatement forStatement = (ForStatement) statement;
                printStatement(forStatement.init, ctx);
                BlockIr currentBlock1 = ctx.generator.currentBlock;
                BlockIr condBlock = ctx.generator.newBlock();
                BlockIr bodyBlock = ctx.generator.newBlock();
                BlockIr postBlock = ctx.generator.newBlock();
                finalBlock = ctx.generator.newBlock();
                ForContext forContext = new ForContext();
                forContext.out = ctx.forContext;
                forContext.postBlock = postBlock;
                forContext.finalBlock = finalBlock;
                ctx.forContext = forContext;
                ctx.generator.changeBlock(currentBlock1);
                ctx.generator.newQuad(null, ctx.generator.newBlockValue(condBlock), null, OPIr.GoToIr);
                ctx.generator.changeBlock(condBlock);
                ExprContext exprContext1 = new ExprContext(ctx);
                exprContext1.orBlock = bodyBlock;
                exprContext1.breakBlock = finalBlock;
                QuadIr quadIr1 = printExpr(forStatement.cond, exprContext1);
                ctx.generator.newQuad(null, quadIr1.res, ctx.generator.newBlockValue(finalBlock), OPIr.IfFalseIr);
                ctx.generator.newQuad(null, ctx.generator.newBlockValue(bodyBlock), null, OPIr.GoToIr);
                ctx.generator.changeBlock(bodyBlock);
                printStatement(forStatement.body, ctx);
                ctx.generator.newQuad(null, ctx.generator.newBlockValue(postBlock), null, OPIr.GoToIr);
                ctx.generator.changeBlock(postBlock);
                printStatement(forStatement.post, ctx);
                ctx.generator.newQuad(null, ctx.generator.newBlockValue(condBlock), null, OPIr.GoToIr);
                ctx.generator.changeBlock(finalBlock);
                if(ctx.forContext != null)
                    ctx.forContext = ctx.forContext.out;
                break;
            case "IncDecStatement":
                IncDecStatement incDecStatement = (IncDecStatement) statement;
                QuadIr quadIr2 = printExpr(incDecStatement.x, new ExprContext(ctx));
                ValueIr valueIr = new ValueIr();
                BasicLit basicLit = new BasicLit();
                basicLit.value = "1";
                basicLit.kind = LitKind.DecimalInt;
                valueIr.value = ctx.generator.newLitVariable(ctx.generator.getSizeOfType(basicLit), basicLit);
                valueIr.type = ValueIr.ValueType.Var;
                switch(incDecStatement.op)
                {
                    case AddAdd: ctx.generator.newQuad(quadIr2.res, quadIr2.res, valueIr, OPIr.AddrIr);break;
                    case SubSub: ctx.generator.newQuad(quadIr2.res, quadIr2.res, valueIr, OPIr.SubIr);break;
                }
                break;
            case "JumpStatement":
                JumpStatement jumpStatement = (JumpStatement) statement;
                switch (jumpStatement.type)
                {
                    case BREAK:
                        if(ctx.forContext != null)
                            ctx.generator.newQuad(null, ctx.generator.newBlockValue(ctx.forContext.finalBlock), null, OPIr.GoToIr);
                        break;
                    case CONTINUE:
                        if(ctx.forContext != null)
                            ctx.generator.newQuad(null, ctx.generator.newBlockValue(ctx.forContext.postBlock), null, OPIr.GoToIr);
                        break;
                }
                break;
        }
    }

    public static void printDecl(DeclStatement decls, Context ctx) throws IllegalAccessException
    {
        for(Decl decl : decls.decls)
        {
            switch (decl.getClass().getName())
            {
                case "ValueDecl":
                    ValueDecl valueDecl = (ValueDecl) decl;
                    if (valueDecl.value.size() != 0)
                    {
                        for (int i = 0; i < valueDecl.value.size(); i++)
                        {
                            QuadIr quad1 = printExpr(valueDecl.names.get(i), null);
                            QuadIr quad2 = printExpr(valueDecl.value.get(i), null);
                            ctx.generator.newQuad(quad1.res, quad2.res, null, OPIr.AssignIr);
                        }
                    }
                    break;
            }
        }
        return;
    }

    public static QuadIr printExpr(Expr expr, ExprContext ctx) throws IllegalAccessException
    {
        if(expr == null)
            return null;
        switch (expr.getClass().getName())
        {
            case "Id":
                Id id = (Id)expr;
                ScopeObject scopeObject = (ScopeObject) id.object;
                switch(scopeObject.kind)
                {
                    case Const: case Var:
                        if(scopeObject == ctx.generator.NullType)
                        {
                            ValueIr valueIr = new ValueIr();
                            Variable nullVar = new Variable();
                            nullVar.name = "nil";
                            nullVar.isGlobal = true;
                            nullVar.globalId = "nil";
                            nullVar.isConst = true;
                            nullVar.type = new TNil();
                            nullVar.vtype = Variable.VType.Nil;
                            valueIr.value = nullVar;
                            valueIr.type = ValueIr.ValueType.Var;
                            return new QuadIr(valueIr, null, null, null);
                        }
                        ValueIr valueIr1 = new ValueIr();
                        valueIr1.value = scopeObject.value;
                        valueIr1.type = ValueIr.ValueType.Var;
                        return new QuadIr(valueIr1, null, null, null);
                    case Type: case Func:
                        String combineId = utils.getCombinedId(ctx.generator.currentPackage.name, id);
                        if(ctx.generator.exportedIds.containsKey(combineId))
                        {
                            ValueIr valueIr = new ValueIr();
                            valueIr.value = ctx.generator.functionIrMeta.get(ctx.generator.exportedIds.get(combineId));
                            valueIr.type = ValueIr.ValueType.Func;
                            return new QuadIr(valueIr, null, null, null);
                        }
                        break;
                    case Pkg:
                        if(ctx.generator.packageIrMeta.containsKey(id))
                        {
                            ValueIr valueIr = new ValueIr();
                            valueIr.value = ctx.generator.packageIrMeta.get(id);
                            valueIr.type = ValueIr.ValueType.Package;
                            return new QuadIr(valueIr, null, null, null);
                        }
                        break;
                }
                break;
            case "SelectorExpr":
                SelectorExpr selectorExpr = (SelectorExpr)expr;
                QuadIr quadIr = printExpr(selectorExpr.expr, ctx);
                switch(quadIr.res.type)
                {
                    case Var:
                        Variable variable = (Variable) quadIr.res.value;
                        StructExpr structExpr = (StructExpr) ctx.generator.getRealType((Expr) variable.type);
                        for(Field field : structExpr.fields)
                        {
                            if(field.id.name.equals(selectorExpr.sel.name))
                            {
                                ValueIr valueIr = new ValueIr();
                                valueIr.value = ctx.generator.newStructLocalVariable(field, variable);
                                valueIr.type = ValueIr.ValueType.Var;
                                return new QuadIr(valueIr, null, null, null);
                            }
                        }
                        Method method = ctx.generator.findMethod(variable.type, selectorExpr.sel);
                        return printExpr(method.funcId, ctx);
                    case Package:
                        String idName = utils.getQualifiedId(selectorExpr);
                        if(ctx.generator.exportedIds.containsKey(idName))
                        {
                            ValueIr valueIr = new ValueIr();
                            valueIr.value = ((ScopeObject)ctx.generator.exportedIds.get(idName).object).value;
                            valueIr.type = ValueIr.ValueType.Var;
                            return new QuadIr(valueIr, null, null, null);
                        }
                        break;
                }
                break;
            case "CallExpr":
                CallExpr callExpr = (CallExpr) expr;
                int argLen = callExpr.args.size();
                QuadIr quadIr1 = printExpr(callExpr.func, ctx);
                ctx.generator.newQuad(null, quadIr1.res, null, OPIr.AllocRetIr);
                for(int i=0; i<argLen; i++)
                {
                    QuadIr quadIr2 = printExpr(callExpr.args.get(i), ctx);
                    ctx.generator.newQuad(null, quadIr2.res, null, OPIr.ParamIr);
                }
                QuadIr call = ctx.generator.newQuad(null, quadIr1.res, null, OPIr.CallIr);
                FuncIr funcIr = (FuncIr)quadIr1.res.value;
                if(ctx.assignContext != null)
                {
                    if(ctx.assignContext.assignValues.size() > 1)
                    {
                        if(funcIr.func.results.size() != ctx.assignContext.assignValues.size())
                        {
                            System.out.print("result num error");
                        }
                        for(int i=0; i<ctx.assignContext.assignValues.size(); i++)
                        {
                            ValueIr valueIr = new ValueIr();
                            valueIr.value = ctx.assignContext.assignValues.get(i);
                            valueIr.type = ValueIr.ValueType.Var;
                            ValueIr param2 = new ValueIr();
                            param2.value = i;
                            param2.type = ValueIr.ValueType.Int;
                            ctx.generator.newQuad(valueIr, quadIr1.res, param2, OPIr.GetCallIr);
                        }
                        ctx.ok = true;
                    }
                    else
                    {
                        ValueIr valueIr = new ValueIr();
                        valueIr.value = 0;
                        valueIr.type = ValueIr.ValueType.Int;
                        return ctx.generator.newQuadWithTmpVar(quadIr1.res, valueIr, OPIr.GetCallIr, funcIr.func.results.get(0).type);
                    }
                }
                else if(funcIr.func.results.size() != 0)
                {
                    ValueIr valueIr = new ValueIr();
                    valueIr.value = 0;
                    valueIr.type = ValueIr.ValueType.Int;
                    return ctx.generator.newQuadWithTmpVar(quadIr1.res, valueIr, OPIr.GetCallIr, funcIr.func.results.get(0).type);
                }
                return call;
            case "BasicLit":
                BasicLit basicLit = (BasicLit) expr;
                switch(basicLit.kind)
                {
                    case ImaginaryInt:case DecimalInt:case BinaryInt:
                    case OctalInt:case HexInt: case RuneInt:
                        ValueIr valueIr = new ValueIr();
                        valueIr.value = ctx.generator.newLitVariable(ctx.generator.getSizeOfType(new TInt()), basicLit);
                        valueIr.type = ValueIr.ValueType.Var;
                        return new QuadIr(valueIr, null, null, null);
                }
                break;
            case "CompositeLit":
                CompositeLit compositeLit = (CompositeLit) expr;
                Object type = ctx.generator.getRealType(compositeLit.type);
                switch (ctx.generator.getKind(compositeLit.type))
                {
                    case "Struct":
                        Variable variable = null;
                        if(ctx.assignContext != null)
                        {
                            variable = ctx.assignContext.assignValues.get(0);
                            AssignContext assignContext = new AssignContext();
                            assignContext.out = ctx.assignContext;
                            ctx.assignContext = assignContext;
                        }
                        StructExpr structExpr = (StructExpr) type;
                        for(Expr expr1 : compositeLit.value)
                        {
                            KeyValueExpr keyValueExpr = (KeyValueExpr) expr1;
                            Id name = (Id) keyValueExpr.key;
                            Field nameField = utils.findStructField(structExpr, name.name);
                            Variable variable1 = ctx.generator.newStructLocalVariable(nameField, variable);
                            ctx.assignContext.assignValues = new ArrayList<>();
                            ctx.assignContext.assignValues.add(variable1);
                            QuadIr quadIr2 = printExpr(keyValueExpr.value, ctx);
                            if(!ctx.ok)
                            {
                                ValueIr valueIr = new ValueIr();
                                valueIr.value = variable1;
                                valueIr.type = ValueIr.ValueType.Var;
                                ctx.generator.newQuad(valueIr, quadIr2.res, null, OPIr.AssignIr);
                            }
                            ctx.ok = false;
                        }
                        ctx.ok = true;
                        if(ctx.assignContext != null)
                            ctx.assignContext = ctx.assignContext.out;
                }
                break;
            case "UnaryExpr":
                UnaryExpr unaryExpr = (UnaryExpr) expr;
                QuadIr quadIr2 = ctx.generator.newEmptyQuad(new TInt());
                Object type1 = ctx.generator.getType(unaryExpr);
                switch(unaryExpr.op)
                {
                    case Add:
                        return ctx.generator.newQuadWithTmpVar(quadIr2.res, printExpr(unaryExpr.x, ctx).res, OPIr.AddIr, type1);
                    case Sub:
                        return ctx.generator.newQuadWithTmpVar(quadIr2.res, printExpr(unaryExpr.x, ctx).res, OPIr.SubIr, type1);
                    case Not:
                        return ctx.generator.newQuadWithTmpVar(quadIr2.res, printExpr(unaryExpr.x, ctx).res, OPIr.NotIr, type1);
                    case Mul:case Xor:
                        return printExpr(unaryExpr.x, ctx);
                    case And:
                        StarType starType = new StarType();
                        starType.type = (Expr) type1;
                        return ctx.generator.newQuadWithTmpVar(printExpr(unaryExpr.x, ctx).res, null, OPIr.AddIr, starType);
                }
                break;
            case "Expression":
                Expression expression = (Expression) expr;
                QuadIr quad1 = printExpr(expression.A, ctx);
                QuadIr quad2 = printExpr(expression.B, ctx);
                Object type2 = ((Variable)quad1.res.value).type;
                switch(expression.op)
                {
                    case Mul:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.MulIr, type2);
                    case Div:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.DivIr, type2);
                    case Mod:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.RemIr, type2);
                    case Shl:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.ShlIr, type2);
                    case Shr:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.ShrIr, type2);
                    case And:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.AndIr, type2);
                    case Add:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.AddIr, type2);
                    case Sub:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.SubIr, type2);
                    case Or:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.OrIr, type2);
                    case Xor:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.XorIr, type2);
                    case Eql:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.EqlIR, type2);
                    case Gtr:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.GtrIr, type2);
                    case Neq:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.NeqIr, type2);
                    case Lss:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.LssIr, type2);
                    case Leq:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.LeqIR, type2);
                    case Geq:
                        return ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.GeqIr, type2);
                    case AndAnd:
                        QuadIr quadIr3 = ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.AndAndIr, type2);
                        if(ctx.breakBlock != null)
                        {
                            ValueIr valueIr = new ValueIr();
                            valueIr.value = ctx.breakBlock;
                            valueIr.type = ValueIr.ValueType.Block;
                            ctx.generator.newQuad(null, quadIr3.res, valueIr, OPIr.IfFalseIr);
                        }
                        return quadIr3;
                    case OrOr:
                        QuadIr quadIr4 = ctx.generator.newQuadWithTmpVar(quad1.res, quad2.res, OPIr.OrOrIr, type2);
                        if(ctx.breakBlock != null)
                        {
                            ValueIr valueIr = new ValueIr();
                            valueIr.value = ctx.orBlock;
                            valueIr.type = ValueIr.ValueType.Block;
                            ctx.generator.newQuad(null, quadIr4.res, valueIr, OPIr.IfTrueIr);
                        }
                        return quadIr4;
                }
                break;
            case "IndexExpr":
                IndexExpr indexExpr = (IndexExpr) expr;
                QuadIr quadIr3 = printExpr(indexExpr.x, ctx);
                QuadIr quadIr4 = printExpr(indexExpr.index, ctx);
                Id id1 = (Id)indexExpr.x;
                ArrayType arrayType = (ArrayType) ((ValueDecl)((ScopeObject)id1.object).field).type;
                if(ctx.assignContext == null)
                {
                    QuadIr retIr = ctx.generator.newQuadWithTmpVar(quadIr3.res, quadIr4.res, OPIr.IndexIr, arrayType.type);
                    return ctx.generator.newQuadWithTmpVar(retIr.res, null, OPIr.AssignIr, arrayType.type);
                }
                else
                {
                    QuadIr retIr = ctx.generator.newQuadWithTmpVar(quadIr3.res, quadIr4.res, OPIr.IndexIr, arrayType.type);
                    ValueIr valueIr = new ValueIr();
                    valueIr.value = ctx.assignContext.assignValues.get(0);
                    valueIr.type = ValueIr.ValueType.Var;
                    return ctx.generator.newQuad(valueIr, retIr.res, null, OPIr.StarIr);
                }
            case "StarType":
                StarType starType = (StarType) expr;
                QuadIr quadIr5 = printExpr(starType.type, ctx);
                return ctx.generator.newQuadWithTmpVar(quadIr5.res, null, OPIr.StarIr, starType.type);
        }
        return null;
    }
}
